package main.algorithms.aco;

import java.util.ArrayList;
import javax.swing.JFrame;

import main.problemstruktur.Knoten;
import main.problemstruktur.Problem;

public class ACS extends AcoMetaHeuristic {
	private Double beta;
	private Double xi;
	private Double rho;
	private Double q0;

	public ACS(Problem problem) {
		super(problem);
	}

	@Override
	protected void initializeStatistics() {
	}

	@Override
	protected void initializeAnts() {
		ants = new ArrayList<Ant>();
		for (int i = 0; i < 10; i++) {
			ants.add(new Ant(problem));
		}
	}

	@Override
	protected void initializeParameters() {
		beta = 2.0;
		rho = 0.1;
		xi = 0.1;
		q0 = 0.9;
		tau0 = 1.0 / (problem.getNodes().size());
	}

	@Override
	protected void makeDecision(Ant ant, int step) {
		Double q = Math.random();
		int choice;
		if (q <= q0) {
			choice = chooseMax(ant, step);
		} else {
			choice = chooseRandom(ant, step);
		}
		ant.setTourInstanze(step, choice);
		decreasePheromon(ant.getTourInstanze(step).getId(), choice);
	}

	private void decreasePheromon(int knotenID, int position) {
		problem.setPheromon(knotenID, position,
				(problem.getPheromon(knotenID, position) * (1.0 - xi))
						+ (tau0 * xi));
	}

	private int chooseRandom(Ant ant, int position) {
		Double randomNumber = Math.random();
		Double pheromonCounter = 0.0;
		Integer choice = null;
		for (Knoten node : problem.getNodes()) {
			if (!ant.getVisited(node.getId())) {
				if (randomNumber > pheromonCounter) {
					choice = problem.getNodes().indexOf(node);
				}
				pheromonCounter += getProbability(node.getId(), ant, position);
			}
		}
		if (choice == null) {
			throw (new NoNodeChoosenException());
		}
		return choice;
	}

	private int chooseMax(Ant ant, int position) {
		Double max = 0.0;
		Integer maxNode = null;
		for (Knoten node : problem.getNodes()) {
			if (!ant.getVisited(node.getId())) {
				if (getSingleProbabilityValue(node.getId(), ant, position) > max) {
					max = getSingleProbabilityValue(node.getId(), ant, position);
					maxNode = node.getId();
				}
			}
		}
		if (maxNode == null) {
			throw (new NoNodeChoosenException());
		}
		return maxNode;
	}

	private Double getSingleProbabilityValue(int nodeID, Ant ant, int position) {
		Double pheromon = problem.getPheromon(nodeID, position);
		Double heuristicValue = ant.getHeuristicalValue(nodeID);
		Double total = pheromon * Math.pow(heuristicValue, beta);
		return total;
	}

	private Double getTotalProbability(int nodeID, Ant ant, int position) {
		Double total = 0.0;
		for (Knoten node : problem.getNodes()) {
			if (!ant.getVisited(node.getId())) {
				total += getSingleProbabilityValue(nodeID, ant, position);
			}
		}
		return total;
	}

	private Double getProbability(int nodeID, Ant ant, int position) {
		return getSingleProbabilityValue(nodeID, ant, position)
				/ getTotalProbability(nodeID, ant, position);
	}

	@Override
	protected void daemonAction() {
		// TODO Auto-generated method stub

	}

	@Override
	protected void updatePheromon() {
		ArrayList<Knoten> nodes = getBestTourSoFar();
		for (int i = 0; i < problem.getNodes().size() - 1; i++) {
			if (nodes.get(i) != null)
				problem.setPheromon(
						nodes.get(i).getId(),
						i,
						(problem.getPheromon(nodes.get(i).getId(), i) * (1.0 - rho))
								+ (calculateNewPheromon(getBestSolutionsGoodness()) * rho));
		}
	}

	private Double calculateNewPheromon(int Goodness) {
		return (1.0 / getBestTourSoFarGoodness() + 1.0);
	}

	@Override
	protected boolean terminate() {
		if (getBestSolutionsGoodness() == 0) {
			System.out.println("Lösung gefunden");
			return true;
		}
		return false;
	}

	@Override
	public int getBestSolutionsGoodness() {
		int goodness = Integer.MAX_VALUE;
		for (Ant ant : ants) {
			if (goodness > ant.getMissingMemberCount()) {
				goodness = ant.getMissingMemberCount();
			}
		}
		return goodness;
	}

	@Override
	public ArrayList<Knoten> getBestSolution() {
		int minIndex = 0;
		int goodness = Integer.MAX_VALUE;
		for (int i = 0; i < ants.size(); i++) {
			if (goodness > ants.get(i).getMissingMemberCount()) {
				goodness = ants.get(i).getMissingMemberCount();
				minIndex = i;
			}
		}
		ArrayList<Knoten> tour = new ArrayList<Knoten>();
		for (int j = 0; j < problem.getNodes().size(); j++) {
			tour.add(ants.get(minIndex).getTourInstanze(j));

		}
		return tour;

	}

	@Override
	protected void makeSolution(Ant ant) {
		ant.updateHeuristics();
		int step = 0;
		while (!ant.isFinished()) {
			makeDecision(ant, step);
			step++;
			ant.updateHeuristics();
		}
	}
}
